package cn.life.common.support.jdbc;


import cn.life.common.support.datasource.DataSourceContextHolder;
import cn.life.common.support.datasource.DataSourceType;
import com.alibaba.druid.pool.DruidDataSource;
import cn.life.common.entity.vo.Pagination;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Description:    抽象支持的数据库类
 *
 * @Author 李佳明
 * @Date 2018/6/18 0018 19:55
 */
public abstract class AbstractSupportDao {

    protected DataSourceType dataSourceType;

    private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static SimpleDateFormat startFormat = new SimpleDateFormat("yyyy-MM-dd 00:00:00");

    private static SimpleDateFormat endFormat = new SimpleDateFormat("yyyy-MM-dd 23:59:59");

    @Autowired
    @Qualifier("mysqlJdbcTemplate")
    private JdbcTemplate mysqlJdbcTemplate;

    @Autowired
    @Qualifier("oracleJdbcTemplate")
    private JdbcTemplate oracleJdbcTemplate;

    protected JdbcTemplate getJdbcTemplate() {
        DataSourceType dataSourceType = DataSourceContextHolder.getDataSource();
        if (dataSourceType == null) {
            return mysqlJdbcTemplate;
        }
        if (dataSourceType == DataSourceType.ORACLE) {
            return oracleJdbcTemplate;
        }
        return mysqlJdbcTemplate;
    }

    /**
     * 使用SQL语句进行单个对象实例查询
     *
     * @param sql         需要查询的SQL语句
     * @param elementType 返回对象的类型
     * @param args        SQL参数
     * @return 指定类的实例
     */
    protected <T> T queryForObject(String sql, Class<T> elementType, Object... args) {
        T result;
        try {
            if (isWrapClass(elementType)) {
                result = getJdbcTemplate().queryForObject(sql, elementType, args);
            } else {
                result = getJdbcTemplate().queryForObject(sql, BeanPropertyRowMapper.newInstance(elementType), args);
            }
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    /**
     * 使用sql语句进行单个对象实例查询
     *
     * @param sql    需要查询的SQL语句
     * @param mapper 映射类型
     * @param args   SQL参数
     * @return 指定类的实例
     */
    protected <T> T queryForObject(String sql, RowMapper<T> mapper, Object... args) {
        T result;
        try {
            result = getJdbcTemplate().queryForObject(sql, mapper, args);
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    /**
     * 使用sql语句进行多个对象实例查询
     *
     * @param sql         需要查询的SQL语句
     * @param elementType 返回对象的类型
     * @param args        SQL参数
     * @return 指定类的实例的集合
     */
    protected <T> List<T> queryForList(String sql, Class<T> elementType, Object... args) {
        List<T> result;
        try {
            if (isWrapClass(elementType)) {
                result = getJdbcTemplate().queryForList(sql, elementType, args);
            } else {
                result = getJdbcTemplate().query(sql, BeanPropertyRowMapper.newInstance(elementType), args);
            }
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    /**
     * 使用sql语句进行多个对象实例查询
     *
     * @param sql    需要查询的SQL语句
     * @param mapper 映射类型
     * @param args   SQL参数
     * @return 指定类的实例的集合
     */
    protected <T> List<T> queryForList(String sql, RowMapper<T> mapper, Object... args) {
        List<T> result;
        try {
            result = getJdbcTemplate().query(sql, mapper, args);
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    /**
     * 使用sql语句进行多个Map对象实例查询
     *
     * @param sql  需要查询的SQL语句
     * @param args SQL参数
     * @return Map实例的集合
     */
    protected List<Map<String, Object>> queryForList(String sql, Object... args) {
        List<Map<String, Object>> result;
        try {
            result = getJdbcTemplate().queryForList(sql, args);
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    /**
     * 使用SQL语句进行查询，查询结果为Map对象
     *
     * @param sql  需要查询的SQL语句
     * @param args SQL参数
     * @return 查询得到的Map对象
     */
    protected Map<String, Object> queryForMap(String sql, Object... args) {
        Map<String, Object> result;
        try {
            result = getJdbcTemplate().queryForMap(sql, args);
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    /**
     * 使用SQL语句进行数据更新
     *
     * @param sql  需要执行的SQL语句
     * @param args SQL参数
     * @return 受影响数据的条数
     */
    protected int update(String sql, Object... args) {
        return getJdbcTemplate().update(sql, args);
    }

    /**
     * 批量执行sql语句执行数据更新
     *
     * @param sql       需要执行的sql语句
     * @param batchArgs sql参数
     * @return 受影响数据的条数
     */
    protected int[] batchUpdate(String sql, List<Object[]> batchArgs) {
        return getJdbcTemplate().batchUpdate(sql, batchArgs);
    }

    /**
     * 执行存储过程语句
     *
     * @param callString 需要执行的CALL SQL语句
     * @param action     回调方法
     * @return 存储过程的返回值
     */
    protected <T> T execute(String callString, CallableStatementCallback<T> action) {
        return getJdbcTemplate().execute(callString, action);
    }

    //========================================================================

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql         需要查询的SQL语句
     * @param orderSql    SqlServer排序语句，ORDER BY StudentID
     * @param elementType 返回对象的类型
     * @param rowFrom     起始记录行号，最小行号1
     * @param rowTo       终止记录行号
     * @param args        sql参数
     * @return 指定类的实例的集合
     */
    protected <T> List<T> queryForListByPage(String sql, String orderSql, Class<T> elementType, int rowFrom, int rowTo, Object... args) {
        String sqlForPage = getPageSqlString(sql, orderSql, rowFrom, rowTo);
        List<T> result;
        try {
            if (isWrapClass(elementType)) {
                result = getJdbcTemplate().queryForList(sqlForPage, elementType, args);
            } else {
                result = getJdbcTemplate().query(sqlForPage, BeanPropertyRowMapper.newInstance(elementType), args);
            }
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql         需要查询的SQL语句
     * @param elementType 返回对象的类型
     * @param rowFrom     起始记录行号，最小行号1
     * @param rowTo       终止记录行号
     * @param args        sql参数
     * @return 指定类的实例的集合
     */
    protected <T> List<T> queryForListByPage(String sql, Class<T> elementType, int rowFrom, int rowTo, Object... args) {
        return queryForListByPage(sql, null, elementType, rowFrom, rowTo, args);
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql      需要查询的SQL语句
     * @param orderSql SqlServer排序语句，ORDER BY StudentID
     * @param mapper   映射类型
     * @param rowFrom  起始记录行号，最小行号1
     * @param rowTo    终止记录行号
     * @param args     SQL参数
     * @return 指定对象实例的集合
     */
    protected <T> List<T> queryForListByPage(String sql, String orderSql, RowMapper<T> mapper, int rowFrom, int rowTo, Object... args) {
        String sqlForPage = getPageSqlString(sql, orderSql, rowFrom, rowTo);
        List<T> result;
        try {
            result = getJdbcTemplate().query(sqlForPage, mapper, args);
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql     需要查询的SQL语句
     * @param mapper  映射类型
     * @param rowFrom 起始记录行号
     * @param rowTo   终止记录行号
     * @param args    SQL参数
     * @return 指定对象实例的集合
     */
    protected <T> List<T> queryForListByPage(String sql, RowMapper<T> mapper, int rowFrom, int rowTo, Object... args) {
        return queryForListByPage(sql, null, mapper, rowFrom, rowTo, args);
    }

    /**
     * 使用SQL语句进行多个Map对象实例的分页查询
     *
     * @param sql      需要查询的SQL语句
     * @param orderSql SqlServer排序语句，ORDER BY StudentID
     * @param rowFrom  起始记录行号，最小行号1
     * @param rowTo    终止记录行号
     * @param args     SQL参数
     * @return Map实例的集合
     */
    protected List<Map<String, Object>> queryForListByPage(String sql, String orderSql, int rowFrom, int rowTo, Object... args) {
        String sqlForPage = getPageSqlString(sql, orderSql, rowFrom, rowTo);
        List<Map<String, Object>> result;
        try {
            result = getJdbcTemplate().queryForList(sqlForPage, args);
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }


    /**
     * 使用sql语句进行多个对象实例查询
     * @param sql 需要查询的sql语句
     * @param elementType 返回对象的类型
     * @param args sql参数
     * @return 指定类的实例的集合
     */
    public <T> List<T> queryForListByjdbc(String sql, Class<T> elementType, Object... args) {
        return queryForList(sql, elementType, args);
    }




    /**
     * 使用SQL语句进行多个Map对象实例的分页查询
     *
     * @param sql     需要查询的SQL语句
     * @param rowFrom 起始记录行号，最小行号1
     * @param rowTo   终止记录行号
     * @param args    SQL参数
     * @return Map实例的集合
     */
    protected List<Map<String, Object>> queryForListByPage(String sql, int rowFrom, int rowTo, Object... args) {
        String sqlForPage = getPageSqlString(sql, null, rowFrom, rowTo);
        List<Map<String, Object>> result;
        try {
            result = getJdbcTemplate().queryForList(sqlForPage, args);
        } catch (EmptyResultDataAccessException e) {
            result = null;
        }
        return result;
    }

    //========================================================================

    /**
     * 查记录的总数
     *
     * @param sql 需要查询的SQL语句
     * @return 记录的总数
     */
    private int countForPage(String sql, Object... args) {
        return getJdbcTemplate().queryForObject("SELECT COUNT(1) FROM (" + sql + ") T1__", Integer.class, args);
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql         需要查询的sql语句
     * @param orderSql    SqlServer排序语句，ORDER BY StudentID
     * @param elementType 返回对象的类型
     * @param total       记录数
     * @param pageIndex   页索引，最小索引=0
     * @param pageSize    页大小
     * @param args        SQL参数
     * @return 指定类的实例的集合
     */
    public <T> Pagination<T> queryForListByPageEx(String sql, String orderSql, Class<T> elementType, int total, int pageIndex, int pageSize, Object... args) {
        Pagination<T> rowList = new Pagination<T>();
        if (total > 0) {
            Pagination<T> pagination = new Pagination<T>(total, pageIndex, pageSize);
            rowList.setTotal(total);
            List<T> list = queryForListByPage(sql, orderSql, elementType, pagination.getRowFrom(), pagination.getRowTo(), args);
            rowList.setRows(list);
        } else {
            rowList.setTotal(0);
            rowList.setRows(new ArrayList<T>());
        }
        return rowList;
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql         需要查询的sql语句
     * @param elementType 返回对象的类型
     * @param total       记录数
     * @param pageIndex   页索引，最小索引=0
     * @param pageSize    页大小
     * @param args        SQL参数
     * @return 指定类的实例的集合
     */
   /* public <T> Pagination<T> queryForListByPageEx(String sql, Class<T> elementType, int total, int pageIndex, int pageSize, Object... args) {
        return queryForListByPageEx(sql, null, elementType, total, pageIndex, pageSize, args);
    }
*/
    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql       需要查询的sql语句
     * @param orderSql  SqlServer排序语句，ORDER BY StudentID
     * @param mapper    映射类型
     * @param total     记录数
     * @param pageIndex 页索引，最小索引=0
     * @param pageSize  页大小
     * @param args      SQL参数
     * @return 指定类的实例的集合
     */
    public <T> Pagination<T> queryForListByPageEx(String sql, String orderSql, RowMapper<T> mapper, int total, int pageIndex, int pageSize, Object... args) {
        Pagination<T> Pagination = new Pagination<T>();
        if (total > 0) {
            Pagination<T> pagination = new Pagination<T>(total, pageIndex, pageSize);
            Pagination.setTotal(total);
            List<T> list = queryForListByPage(sql, orderSql, mapper, pagination.getRowFrom(), pagination.getRowTo(), args);
            Pagination.setRows(list);
        } else {
            Pagination.setTotal(0);
            Pagination.setRows(new ArrayList<T>());
        }
        return Pagination;
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql       需要查询的sql语句
     * @param mapper    映射类型
     * @param total     记录数
     * @param pageIndex 页索引，最小索引=0
     * @param pageSize  页大小
     * @param args      SQL参数
     * @return 指定类的实例的集合
     */
    public <T> Pagination<T> queryForListByPageEx(String sql, RowMapper<T> mapper, int total, int pageIndex, int pageSize, Object... args) {
        return queryForListByPageEx(sql, null, mapper, total, pageIndex, pageSize, args);
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql         需要查询的SQL语句
     * @param orderSql    SqlServer排序语句，ORDER BY StudentID
     * @param elementType 返回对象的类型
     * @param pageIndex   页索引，最小索引=0
     * @param pageSize    页大小
     * @param args        SQL参数
     * @return 指定类的实例的集合
     */
    protected <T> Pagination<T> queryForListByPageEx(String sql, String orderSql, Class<T> elementType, int pageIndex, int pageSize, Object... args) {
        int total = countForPage(sql, args);
        return queryForListByPageEx(sql, orderSql, elementType, total, pageIndex, pageSize, args);
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql         需要查询的SQL语句
     * @param elementType 返回对象的类型
     * @param pageIndex   页索引，最小索引=0
     * @param pageSize    页大小
     * @param args        SQL参数
     * @return 指定类的实例的集合
     */
    protected <T> Pagination<T> queryForListByPageEx(String sql, Class<T> elementType, int pageIndex, int pageSize, Object... args) {
        int total = countForPage(sql, args);
        return queryForListByPageEx(sql, null, elementType, total, pageIndex, pageSize, args);
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql         需要查询的SQL语句
     * @param elementType 返回对象的类型
     * @param pageIndex   页索引，最小索引=0
     * @param pageSize    页大小
     * @param args        SQL参数
     * @return 指定类的实例的集合
     */
    protected <T> Pagination<T> queryMyForListByPageEx(String sql, Class<T> elementType, int pageIndex, int pageSize, Object... args) {
        int total = countForPage(sql, args);
        return queryForListByPageEx(sql, null, elementType, total, pageIndex, pageSize, args);
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql       需要查询的SQL语句
     * @param orderSql  SqlServer排序语句，ORDER BY StudentID
     * @param mapper    映射类型
     * @param pageIndex 页索引，最小索引=0
     * @param pageSize  页大小
     * @param args      SQL参数
     * @return 指定类的实例的集合
     */
    protected <T> Pagination<T> queryForListByPageEx(String sql, String orderSql, RowMapper<T> mapper, int pageIndex, int pageSize, Object... args) {
        int total = countForPage(sql, args);
        return queryForListByPageEx(sql, orderSql, mapper, total, pageIndex, pageSize, args);
    }

    /**
     * 使用SQL语句进行多个对象实例的分页查询
     *
     * @param sql       需要查询的SQL语句
     * @param mapper    映射类型
     * @param pageIndex 页索引，最小索引=0
     * @param pageSize  页大小
     * @param args      SQL参数
     * @return 指定类的实例的集合
     */
    protected <T> Pagination<T> queryForListByPageEx(String sql, RowMapper<T> mapper, int pageIndex, int pageSize, Object... args) {
        int total = countForPage(sql, args);
        return queryForListByPageEx(sql, null, mapper, total, pageIndex, pageSize, args);
    }

    //========================================================================

    protected String getPageSqlString(String sql, String order, int from, int to) {
        String sqlForPage = null;
        switch (getDataSourceType()) {
            case ORACLE:
                //ROWNUM只支持小于，大于是不支持的，因此要做如下定义
                sqlForPage = String.format("SELECT * FROM (SELECT ROWNUM AS RN__,T1__.* FROM (%s) T1__ WHERE ROWNUM<=%d) WHERE RN__>=%d", sql, to, from);
                break;
            case MYSQL:
                sqlForPage = String.format("%s LIMIT %d,%d", sql, from, to - from);
                break;
//            case DSTYPE_SQLSERVER:
//                sqlForPage = String.format("SELECT * FROM (SELECT ROW_NUMBER() OVER (%s) AS RowNumber__,* FROM (%s) T1__) T2__ WHERE RowNumber__>=%d AND RowNumber__<=%d", order, sql, from, to);
//                break;
            default:
                break;
        }
        return sqlForPage;
    }

    /**
     * 指定的类型是否是包装类
     *
     * @param cls 指定的类型
     * @return 类型是否是包装类
     */
    protected boolean isWrapClass(Class<?> cls) {
        boolean result;
        if (cls == java.sql.Blob.class ||
                cls == java.sql.Clob.class) {
            return true;
        }
        try {
            Field field = cls.getField("TYPE");
            Class<?> type = (Class<?>) field.get(null);
            result = type.isPrimitive();
        } catch (Exception e) {
            result = false;
        }
        return result ? result : cls.equals(String.class);
    }

    protected DataSourceType getDataSourceType() {
        if (dataSourceType == null) {
            DruidDataSource ds = (DruidDataSource) getJdbcTemplate().getDataSource();
            String driverClassName = ds.getDriverClassName();
            switch (driverClassName) {
                case "com.mysql.jdbc.Driver":
                    dataSourceType = DataSourceType.MYSQL;
                    break;
                case "oracle.jdbc.driver.OracleDriver":
                    dataSourceType = DataSourceType.ORACLE;
                    break;
                default:
                    break;
            }
        }
        return dataSourceType;
    }

    public static String getNowDate(){
        return dateFormat.format(new Date());
    }

    public static String getSqlDate(Date date){
        return dateFormat.format(date);
    }

    public static String getStartDate(Date date){
        return startFormat.format(date);
    }

    public static String getEndDate(Date date){
        return endFormat.format(date);
    }

    public Long queryTableNextId(String tableName,String idName){
        String sql = "select ifnull(max(" + idName + "),0) from " + tableName + "";
        return queryForObject(sql,Long.class) + 1;
    }

    public String getProjectNo(Long factoryId,Long projectId){
        String prefix = Long.valueOf(20000 + factoryId).toString();
        String context =  "00" + projectId;
        return prefix + context;
    }
}
